package track2.bayes;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map.Entry;

import track2.context.Config;
import track2.data.*;

/**
 * Old Version
 * Training Class for NaiveBayes
 * @author lingjuan, yijia
 *
 */
public class CopyOfTrainClassifier {
	private static HashMap<String, Double> titleC;
	private static HashMap<String, Double> titleA;
	private static HashMap<String, Double> queryC;
	private static HashMap<String, Double> queryA;
	private static HashMap<String, Double> descriptionC;
	private static HashMap<String, Double> descriptionA;
	private static HashMap<String, Double> purchaseC;
	private static HashMap<String, Double> purchaseA;
	private static HashMap<String, Double> genderC;
	private static HashMap<String, Double> genderA;
	private static HashMap<String, Double> ageC;
	private static HashMap<String, Double> ageA;
	
	/**
	 * Main Function for training
	 * Call this to get a {@link NaiveBayesFeatures}
	 * @return NaiveBayesFeatures
	 */
	public static NaiveBayesFeatures train(){
		NaiveBayesFeatures nbFeatures = new NaiveBayesFeatures();
		
		initial();
		// get the counts for each feature
		trainFeature(Config.trainingFilePath);

		// compute the probability for each feature;
		// Flag "T": title; "Q": query, "D": description, "P":purchaseKeyword,
		// "U": user
		computeProbability(nbFeatures, "T");
		computeProbability(nbFeatures, "Q");
		computeProbability(nbFeatures, "D");
		computeProbability(nbFeatures, "P");
		computeProbability(nbFeatures, "U");
		
		return nbFeatures;
	}

	private static void initial() {
		titleC = new HashMap<String, Double>();
		titleA = new HashMap<String, Double>();
		queryC = new HashMap<String, Double>();
		queryA = new HashMap<String, Double>();
		descriptionC = new HashMap<String, Double>();
		descriptionA = new HashMap<String, Double>();
		purchaseC = new HashMap<String, Double>();
		purchaseA = new HashMap<String, Double>();
		genderC = new HashMap<String, Double>();
		genderA = new HashMap<String, Double>();
		ageC = new HashMap<String, Double>();
		ageA = new HashMap<String, Double>();
	}

	private static void trainFeature(String trainingFile) {
		try {
			BufferedReader br = new BufferedReader(new FileReader(
					Config.trainingFilePath));
			String line;
			while ((line = br.readLine()) != null) {
				String[] paras = line.split("\t");// id seg1|seg2|seg3....
				double click = Double.parseDouble(paras[0]);
				double impression = Double.parseDouble(paras[1]);
				int queryID = Integer.parseInt(paras[7]);
				int purchaseID = Integer.parseInt(paras[8]);
				int titleID = Integer.parseInt(paras[9]);
				int descriptionID = Integer.parseInt(paras[10]);
				int userID = Integer.parseInt(paras[11]);

				// get all the tokens based on the hashmap created in
				// track2.data
				String query = getToken("Q", queryID);
				String title = getToken("T", titleID);
				String purchase = getToken("P", purchaseID);
				String description = getToken("D", descriptionID);
				String user = getToken("U", userID);

				// update the training data for each feature.
				update("Q", query, click, impression);
				update("T", title, click, impression);
				update("P", purchase, click, impression);
				update("D", description, click, impression);
				update("U", user, click, impression);

			}
			br.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	// update function: after get the value from hashmap in track2.data, we need
	// to update the hashmap of each training feature.
	private static void update(String flag, String value, double click,
			double impression) {
		String[] sp = value.split("\\|");
		for (int i = 0; i < sp.length; i++) {
			if (flag.equals("Q")) {// Query
				updateMap(queryC, sp[i], click);
				updateMap(queryA, sp[i], impression);
			} else if (flag.equals("T")) {// Title
				updateMap(titleC, sp[i], click);
				updateMap(titleA, sp[i], impression);
			} else if (flag.equals("P")) {// Purchase
				updateMap(purchaseC, sp[i], click);
				updateMap(purchaseA, sp[i], impression);
			} else if (flag.equals("D")) {// Description
				updateMap(descriptionC, sp[i], click);
				updateMap(descriptionA, sp[i], impression);
			}
		}
		// User is different since sp[0] is gender and sp[1] is age
		if (flag.equals("U")) {// User
			updateMap(genderC, sp[0], click);
			updateMap(genderA, sp[0], impression);
			updateMap(ageC, sp[1], click);
			updateMap(ageA, sp[1], impression);
		}

	}

	private static void updateMap(HashMap<String, Double> map, String key,
			double addTimes) {
		if (map.containsKey(key))
			map.put(key, map.get(key) + addTimes);
		else if (!map.containsKey(key)) {
			map.put(key, addTimes);
		}

	}

	// get the tokenId from HashMap in track2.data
	/*
	 * to get a map: TitleMap = TitleToken.getInstance().getMap()
	 */
	private static String getToken(String flag, int id) {
		String result = null;
		if (flag.equals("Q")) {// QueryID
			result = QueryToken.getInstance().getMap().get(id);
		} else if (flag.equals("T")) {// TitleID
			result = TitleToken.getInstance().getMap().get(id);
		} else if (flag.equals("P")) {// PurchaseID
			result = PurchaseKeywordToken.getInstance().getMap().get(id);
		} else if (flag.equals("D")) {// DescriptionID
			result = DescriptionToken.getInstance().getMap().get(id);
		} else if (flag.equals("U")) {// UserID
			result = UserProfile.getInstance().getMap().get(id);
		}
		return result;
	}

	
	private static void computeProbability(NaiveBayesFeatures bayes, String flag) {
		if (flag.equals("Q")) {
			bayes.queryClick = new double[Config.tokenSize];
			for (int i = 0; i < bayes.queryClick.length; i++)
				bayes.queryClick[i] = 1/Config.tokenSize;
			compute(bayes.queryClick, queryC, queryA, Config.tokenSize);
		} else if (flag.equals("T")) {
			bayes.titleClick = new double[Config.tokenSize];
			compute(bayes.titleClick, titleC, titleA, Config.tokenSize);
		} else if (flag.equals("P")) {
			bayes.purchaseClick = new double[Config.tokenSize];
			compute(bayes.purchaseClick, purchaseC, purchaseA, Config.tokenSize);
		} else if (flag.equals("D")) {
			bayes.descriptionClick = new double[Config.tokenSize];
			compute(bayes.descriptionClick, descriptionC, descriptionA,
					Config.tokenSize);
		} else if (flag.equals("U")) {// UserID
			bayes.genderClick = new double[3];
			bayes.ageClick = new double[6];
			compute(bayes.genderClick, genderC, genderA, 3.0);
			compute(bayes.ageClick, ageC, ageA, 6.0);
		}
	}

	//compute probability using formula and store in NaiveBayesFeatures
	private static void compute(double[] prob, HashMap<String, Double> mapC,
			HashMap<String, Double> mapA, double size) {
		java.util.Iterator<Entry<String, Double>> iter = mapC.entrySet()
				.iterator();
		while (iter.hasNext()) {
			Entry<String, Double> entry = iter.next();
			String key = entry.getKey();
			double value = entry.getValue();
			double totalValue = mapA.get(key);
			double probability = (value + 1) / (totalValue + (size-1) * 1.0);
			int index = Integer.parseInt(key);
			prob[index] = probability;
		}

	}


	public static void main(String[] args) {
		initial();
		// get the counts for each feature
		trainFeature(Config.trainingFilePath);

		// compute the probability for each feature;
		// Flag "T": title; "Q": query, "D": description, "P":purchaseKeyword,
		// "U": user
		/*
		computeProbability("T");
		computeProbability("Q");
		computeProbability("D");
		computeProbability("P");
		computeProbability("U");
		*/
	}
}
